Podman:一种更安全的运行容器的方式

Podman 使用传统的 fork/exec 模型(而不是客户端/服务器模型)来运行容器。
397 位读者喜欢这篇文章。

在深入本文的主要内容之前,即 Podman 和容器,我需要稍微介绍一下 Linux 的审计功能。

什么是审计?

Linux 内核有一个有趣的安全性功能,称为 审计 。 它允许管理员监视系统上的安全事件,并将它们记录到 audit.log 中,audit.log 可以本地存储,也可以远程存储在另一台机器上,以防止黑客试图掩盖其踪迹。

/etc/shadow 文件是一个常见的需要监视的安全文件,因为向其中添加记录可能允许攻击者重新访问系统。 管理员想知道是否有任何进程修改了该文件。 您可以通过执行以下命令来完成此操作

# auditctl -w /etc/shadow

现在让我们看看如果我修改 /etc/shadow 文件会发生什么

# touch /etc/shadow

# ausearch -f /etc/shadow -i -ts recent

type=PROCTITLE msg=audit(10/10/2018 09:46:03.042:4108) : proctitle=touch /etc/shadow

type=SYSCALL msg=audit(10/10/2018 09:46:03.042:4108) : arch=x86_64 syscall=openat

success=yes exit=3 a0=0xffffff9c a1=0x7ffdb17f6704 a2=O_WRONLY|O_CREAT|O_NOCTTY|

O_NONBLOCK a3=0x1b6 items=2 ppid=2712 pid=3727 auid=dwalsh uid=root gid=root

euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts1 ses=3 comm=touch

exe=/usr/bin/touch subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)

审计记录中有很多信息,但我强调的是,它记录了 root 修改了 /etc/shadow 文件,并且该进程的审计 UID ( auid ) 的所有者是 dwalsh

内核做到了吗?

跟踪登录 UID

有一个名为 loginuid 的字段,存储在 /proc/self/loginuid 中,它是系统上每个进程的 proc 结构的一部分。 此字段只能设置一次; 设置后,内核将不允许任何进程重置它。

当我登录系统时,登录程序会为我的登录进程设置 loginuid 字段。

我的 UID,dwalsh,是 3267。

$ cat /proc/self/loginuid

3267

现在,即使我成为 root,我的登录 UID 仍然相同。

$ sudo cat /proc/self/loginuid

3267

请注意,从初始登录进程派生和执行的每个进程都会自动继承 loginuid。 这就是内核知道登录的人是 dwalsh 的方式。

容器

现在让我们看看容器。

sudo podman run fedora cat /proc/self/loginuid

3267

即使容器进程也保留了我的 loginuid。 现在让我们尝试 Docker。

sudo docker run fedora cat /proc/self/loginuid

4294967295

为什么有区别?

Podman 对容器使用传统的 fork/exec 模型,因此容器进程是 Podman 进程的后代。 Docker 使用客户端/服务器模型。 我执行的 docker 命令是 Docker 客户端工具,它通过客户端/服务器操作与 Docker 守护程序通信。 然后 Docker 守护程序创建容器并处理 stdin/stdout 与 Docker 客户端工具的通信。

进程的默认 loginuid(在其 loginuid 设置之前)是 4294967295。 由于容器是 Docker 守护程序的后代,而 Docker 守护程序是 init 系统的子进程,因此我们看到 systemd、Docker 守护程序和容器进程都具有相同的 loginuid,4294967295,审计将其称为 *unset* 审计 UID。

cat /proc/1/loginuid

4294967295

如何滥用它?

让我们看看如果 Docker 启动的容器进程修改了 /etc/shadow 文件会发生什么。

$ sudo docker run --privileged -v /:/host fedora touch /host/etc/shadow

$ sudo ausearch -f /etc/shadow -i

type=PROCTITLE msg=audit(10/10/2018 10:27:20.055:4569) : proctitle=/usr/bin/coreutils

--coreutils-prog-shebang=touch /usr/bin/touch /host/etc/shadow

type=SYSCALL msg=audit(10/10/2018 10:27:20.055:4569) : arch=x86_64 syscall=openat

success=yes exit=3 a0=0xffffff9c a1=0x7ffdb6973f50 a2=O_WRONLY|O_CREAT|O_NOCTTY|

O_NONBLOCK a3=0x1b6 items=2 ppid=11863 pid=11882 auid=unset uid=root gid=root

euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=unset

comm=touch exe=/usr/bin/coreutils subj=system_u:system_r:spc_t:s0 key=(null)

在 Docker 的情况下,auid 是 unset (4294967295); 这意味着安全官员可能知道某个进程修改了 /etc/shadow 文件,但身份已丢失。

如果攻击者随后删除了 Docker 容器,系统上将不会有任何人修改 /etc/shadow 文件的痕迹。

现在让我们看看 Podman 的完全相同的情况。

$ sudo podman run --privileged -v /:/host fedora touch /host/etc/shadow

$ sudo ausearch -f /etc/shadow -i

type=PROCTITLE msg=audit(10/10/2018 10:23:41.659:4530) : proctitle=/usr/bin/coreutils

--coreutils-prog-shebang=touch /usr/bin/touch /host/etc/shadow

type=SYSCALL msg=audit(10/10/2018 10:23:41.659:4530) : arch=x86_64 syscall=openat

success=yes exit=3 a0=0xffffff9c a1=0x7fffdffd0f34 a2=O_WRONLY|O_CREAT|O_NOCTTY|

O_NONBLOCK a3=0x1b6 items=2 ppid=11671 pid=11683 auid=dwalsh uid=root gid=root

euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=3 comm=touch

exe=/usr/bin/coreutils subj=unconfined_u:system_r:spc_t:s0 key=(null)

由于 Podman 使用传统的 fork/exec,因此一切都已正确记录。

这只是一个监视 /etc/shadow 文件的简单示例,但是审计系统对于监视进程在系统上所做的事情非常强大。 使用 fork/exec 容器运行时启动容器(而不是客户端/服务器容器运行时)允许您通过审计日志记录来保持更好的安全性。

最后的想法

与启动容器时的客户端/服务器模型相比,fork/exec 模型还有许多其他不错的功能。 例如,systemd 功能包括

  • SD_NOTIFY: 如果您将 Podman 命令放入 systemd 单元文件中,则容器进程可以通过 Podman 将服务已准备好接收任务的通知返回到堆栈中。 这是在客户端/服务器模式下无法完成的事情。
  • Socket 激活: 您可以将 systemd 中的已连接套接字传递给 Podman 并传递给容器进程以供使用。 这在客户端/服务器模型中是不可能的。

我认为最好的功能是以非 root 用户身份运行 Podman 和容器。 这意味着您永远不必向用户授予主机上的 root 权限,而在客户端/服务器模型(如 Docker 采用的模型)中,您必须打开一个套接字到以 root 身份运行的特权守护程序才能启动容器。 在那里,您只能受守护程序中实施的安全机制与主机操作系统中实施的安全机制的支配,这是一种危险的主张。

User profile image.
Daniel Walsh 从事计算机安全领域近 30 年。 Dan 于 2001 年 8 月加入 Red Hat。

4 条评论

感谢您发布这篇文章,Dan。 Podman 在 selinux 方面是否也比 docker 更有优势? 看起来 fork/exec 可以为容器提供更直接的进入 selinux 域的入口。

我们可以更好地处理一些转换规则 Docker Daemon。 但是,由于我添加并维护了 Docker/Moby 中的 SELinux 工作,我也希望保持它们是同类产品中最好的。

回复 作者:david c (未验证)

感谢您的帖子。 很好的解释,简单易懂 Podman 和 Docker 之间的区别。

有史以来最好的帖子

知识共享许可协议本作品采用知识共享署名 - 相同方式共享 4.0 国际许可协议授权。
© . All rights reserved.